/*
* $Id: FamilyManager.java 278 2011-03-04 22:31:44Z volok-aleksej@yandex.ru $
*
* Copyright 2008-2011 by Volokitin Alexey
* All rights reserved
*
*/
package apiary.db.managers;

import java.util.*;
import java.io.*;

import apiary.Apiary;
import apiary.ui.journal.FamilyForm.FamilyValue;
import apiary.db.FamilyStore;
import apiary.db.FamilyStore.FamilyRecord;
import apiary.ui.journal.UserFieldForm.UserField;

/**
 * Family database manager
 * @author lexus
 */
public class FamilyManager
{
    /** instance class */
    private static FamilyManager instance = null;
    /**
     * Gets instance class
     * @return instance class
     */
    protected static FamilyManager getInstance() {
        return instance;
    }

    /** family database */
    private FamilyStore familyStore;
    /** maximal identificator value */
    private int maxIdFamily;
    /** list of families */
    private Vector dbFamily;
    /** list of family's types */
    private Hashtable types;

    /**
     * Constructor
     */
    public FamilyManager() {
        instance = this;
        familyStore = new FamilyStore();
        dbFamily = new Vector();
        types = new Hashtable();
        readFamily();
        hashTypes();
    }

    /**
     * Gets types of family
     * @return list of types
     */
    protected Hashtable getTypes() {
        return types;
    }

    /**
     * Gets family on identificator
     * @param id identificator family
     * @return family
     */
    protected FamilyRecord getFamily(int id) {
        for(int i = 0; i < dbFamily.size(); i++) {
            FamilyRecord family = (FamilyRecord) dbFamily.elementAt(i);
            if(family.idFamily == id)
                return family;
        }
        return null;
    }

    /**
     * Gets families that are located in point
     * @param idPoint identificator of point
     * @return list of families
     */
    protected Vector getFamilies(int idPoint) {
        Vector vector = new Vector();
        for(int i = 0; i < dbFamily.size(); i++) {
            FamilyRecord family = (FamilyRecord) dbFamily.elementAt(i);
            if(family.idPoint == idPoint)
                vector.addElement(family);
        }
        return vector;
    }

    /**
     * Adds family at list.
     * Data will be parsed and created new family object.
     * @param data binary data that describe family
     */
    protected void addFamily(byte[] data) {
        FamilyRecord familyRecord = (FamilyRecord) familyStore.getRecord();
        int offset = familyRecord.setData(data);
        dbFamily.addElement(familyRecord);
        if(maxIdFamily < familyRecord.idFamily)
            maxIdFamily = familyRecord.idFamily;
        FieldsValueManager.getInstance().addFieldValues(data, offset);
    }

    /**
     * Changes family at list.
     * Data will be parsed and changed family object.
     * @param data binary data that describe family
     */
    protected void changeFamily(byte[] data) {
        FamilyRecord familyRecord = (FamilyRecord) familyStore.getRecord();
        familyRecord.setData(data);
        FamilyRecord family = getFamily(familyRecord.idFamily);
        if(family == null)
            return;
        int offset = family.setData(data);
        FieldsValueManager.getInstance().changeFieldValues(data, offset);
    }
    
    /**
     * Delete family from list
     * @param value binary data that describe family
     */
    protected void deleteFamily(byte[] value) {
        try {
            ByteArrayInputStream bais = new ByteArrayInputStream(value);
            DataInputStream dis = new DataInputStream(bais);
            deleteFamilyFromList(dis.readInt());
        } catch (IOException ex) {}
    }

    /**
     * Adds family at list.
     * Converts family to database format and adds at list.
     * @param family family value
     * @return binary data that describe family
     */
    protected byte[] addFamily(FamilyValue family) {
        maxIdFamily = nextIdFamily();
        int idRecord = 0;
        if(family.userFields.size() > 0)
            idRecord = FieldsValueManager.getInstance().nextIdRecord();
        FamilyRecord familyRecord = (FamilyRecord) familyStore.getRecord();
        familyRecord.idFamily = maxIdFamily;
        familyRecord.idRecord = idRecord;
        ConvertData.FamilyValueToRecord(family, familyRecord);
        dbFamily.addElement(familyRecord);
        try {
            ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
            outputStream.write(familyRecord.getData());
            for(int i = 0; i < family.userFields.size(); i++) {
                UserField field = (UserField) family.userFields.elementAt(i);
                FieldsValueManager fvm = FieldsValueManager.getInstance();
                byte[] fieldValue = fvm.addFieldValue(field, idRecord);
                if(fieldValue == null)
                    continue;
                outputStream.write(fieldValue);
            }
            return outputStream.toByteArray();
        } catch (IOException ex) {}
        return null;
    }
    
    /**
     * Changes family at list.
     * Converts family to database format and changes at list.
     * @param id identificator family
     * @param family family value
     * @return binary data that describe family
     */
    protected byte[] changeFamily(int id, FamilyValue family) {
        int idRecord = 0;
        FamilyRecord familyRecord = getFamily(id);
        if(familyRecord == null)
            return null;
        if(family.userFields.size() > 0 && familyRecord.idRecord == 0)
            idRecord = FieldsValueManager.getInstance().nextIdRecord();
        else if(family.userFields.size() > 0 && familyRecord.idRecord != 0)
            idRecord = familyRecord.idRecord;
        familyRecord.idRecord = idRecord;
        ConvertData.FamilyValueToRecord(family, familyRecord);
        try {
            ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
            outputStream.write(familyRecord.getData());
            for(int i = 0; i < family.userFields.size(); i++) {
                UserField field = (UserField) family.userFields.elementAt(i);
                byte[] fieldValue = FieldsValueManager.getInstance().changeFieldValue(field, idRecord);
                if(fieldValue == null)
                    continue;
                outputStream.write(fieldValue);
            }
            return outputStream.toByteArray();
        } catch (IOException ex) {}
        return null;
    }

    /**
     * Delete family from list
     * @param id identificator family
     */
    protected byte[] deleteFamily(int id) {
        try {
            deleteFamilyFromList(id);
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            DataOutputStream dos = new DataOutputStream(baos);
            dos.writeInt(id);
            return baos.toByteArray();
        } catch (IOException ex) {}
        return null;
    }
 
    /**
     * Calculates next family identificator
     * @return identificator
     */
    protected int nextIdFamily() {
        return maxIdFamily + 1;
    }
    /**
     * Delete families from list
     * @param idPoint identificator point
     */
    protected void deleteFamilies(int idPoint) {
        for(int i = 0; i < dbFamily.size(); i++) {
            FamilyRecord familyRecord = (FamilyRecord) dbFamily.elementAt(i);
            if(familyRecord.idPoint == idPoint) {
                FieldsValueManager flmManager = FieldsValueManager.getInstance();
                flmManager.deleteValues(familyRecord.idRecord);
                InspectManager inspectManager = InspectManager.getInstance();
                inspectManager.deleteInspects(familyRecord.idFamily);
                dbFamily.removeElementAt(i);
                i--;
            }
        }
    }

    /**
     * Resets all data in manager
     */
    protected void resetManager() {
        dbFamily.removeAllElements();
        maxIdFamily = 0;
        readFamily();
    }

    /**
     * Reads family database
     */
    private void readFamily() {
        Enumeration familyEnum = familyStore.getRecordsEnumeration();
        do {
            if(!familyEnum.hasMoreElements())
                break;
            FamilyRecord record = (FamilyRecord) familyEnum.nextElement();
            dbFamily.addElement(record);
            if(maxIdFamily < record.idFamily)
                maxIdFamily = record.idFamily;
        } while(true);
    }

   /**
     * Delete family from list
     * @param id identificator family
     */
    private void deleteFamilyFromList(int id) {
        for(int i = 0; i < dbFamily.size(); i++) {
            FamilyRecord familyRecord = (FamilyRecord) dbFamily.elementAt(i);
            if(familyRecord.idFamily == id) {
                FieldsValueManager flmManager = FieldsValueManager.getInstance();
                flmManager.deleteValues(familyRecord.idRecord);
                InspectManager inspectManager = InspectManager.getInstance();
                inspectManager.deleteInspects(familyRecord.idFamily);
                dbFamily.removeElementAt(i);
                break;
            }
        }
    }
    /**
     * Fills types of family
     */
    private void hashTypes() {
        Apiary apiary = Apiary.getInstance();
        types.put(new Integer(FamilyRecord.TYPE_DEFAULT),
                  apiary.getLocaleString("TypeDefault"));
        types.put(new Integer(FamilyRecord.TYPE_PEDIGREE),
                  apiary.getLocaleString("TypePedigree"));
        types.put(new Integer(FamilyRecord.TYPE_BREEDING),
                  apiary.getLocaleString("TypeBreeding"));
        types.put(new Integer(FamilyRecord.TYPE_FATHER),
                  apiary.getLocaleString("TypeFather"));
        types.put(new Integer(FamilyRecord.TYPE_AMADOU),
                  apiary.getLocaleString("TypeAmadou"));
        types.put(new Integer(FamilyRecord.TYPE_BARREN),
                  apiary.getLocaleString("TypeBarren"));
        types.put(new Integer(FamilyRecord.TYPE_QUEEN_CELL),
                  apiary.getLocaleString("TypeQueenCell"));
        types.put(new Integer(FamilyRecord.TYPE_QUEEN_CELL_SMALL),
                  apiary.getLocaleString("TypeQueenCellSmall"));
        types.put(new Integer(FamilyRecord.TYPE_INDIVIDUAL_PROVINE),
                  apiary.getLocaleString("TypeIndividualProvine"));
        types.put(new Integer(FamilyRecord.TYPE_COMBINED_PROVINE),
                  apiary.getLocaleString("TypeCombinedProvine"));
        types.put(new Integer(FamilyRecord.TYPE_PACKET),
                  apiary.getLocaleString("TypePacket"));
        types.put(new Integer(FamilyRecord.TYPE_NON_QUEEN),
                  apiary.getLocaleString("TypeNonQueen"));
        types.put(new Integer(FamilyRecord.TYPE_SWARM),
                  apiary.getLocaleString("TypeSwarm"));
        types.put(new Integer(FamilyRecord.TYPE_USER),
                  apiary.getLocaleString("TypeUser"));
    }
}
